﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace wontec_claw
{
    class sersonhelper
    {
        int readhex(string str, int num, int offset)
        {
            int index = offset;
            int value = 0;
            while (num-- != 0)
            {
                value <<= 4;
                value |= str[index] > '9' ? str[index] - ('A' - 10) : str[index] - '0';
                index++;
            }
            return value;
        }

        public int formatRead(string str, string format, int[] data)
        {
            int readcount = 0;
            int len = format.Length;
            int offset = 0;
            int index = 0;
            int num;
            for (int i = 0; i < format.Length; i++)
            {
                if (format[i] == '%')
                {
                    num = 1;
                    if (format[i + 1] >= '0' && format[i + 1] <= '9')
                    {
                        i++;
                        num = format[i] - '0';
                    }
                    switch (format[i + 1])
                    {
                        case 'c':
                            data[index++] = str[offset];
                            break;
                        case 'd': break;
                        case 'x':
                            data[index++] = readhex(str, num, offset);
                            break;
                        case 's':

                            break;
                    }
                    offset += num;
                    i++;
                    readcount++;
                    continue;
                }
                if (format[i] != str[offset])
                {
                    break;
                }
                offset++;
            }
            return readcount;
        }

        public string formatWrite(char key,int id, string format, int[] data)
        {
            int readcount = 0;
            int len = format.Length;
            int offset = 0;
            int index = 0;
            int num;
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < format.Length; i++)
            {
                if (format[i] == '%')
                {
                    sb.Append('{');
                    num = 1;
                    if (format[i + 1] >= '0' && format[i + 1] <= '9')
                    {
                        i++;
                        num = format[i] - '0';
                    }
                    sb.Append((char)('0' + readcount));
                    switch (format[i + 1])
                    {
                        case 'c':
                            index++;
                            break;
                        case 'd': break;
                        case 'x':
                            sb.Append(":X" + (char)('0'+num));
                            index++;
                            break;
                        case 's':

                            break;
                    }
                    sb.Append('}');
                    offset += num;
                    i++;
                    readcount++;
                    continue;
                }
                else
                {
                    sb.Append(format[i]);
                }
                offset++;
            }
            if (index>0)
            {
                var strdata = new int[10];
                for (int i = 0; i < data.Length; i++) strdata[i] = data[i];
                return string.Format(sb.ToString(),id,key, strdata[0], strdata[1], strdata[2], strdata[3], strdata[4], strdata[5], strdata[6], strdata[7], strdata[8], strdata[9]);
            }
            return sb.ToString();
        }

        Dictionary<char, string> senddatatype = new Dictionary<char, string>();
        Dictionary<char, string> recvdatatype = new Dictionary<char, string>();
        Dictionary<char, int> recvdatalen = new Dictionary<char, int>();
        Dictionary<char, int> recvpacketlen = new Dictionary<char, int>();
        Dictionary<char, Action<char, int, int[]>> recvcallback = new Dictionary<char, Action<char, int, int[]>>();

        int packetlen(string str)
        {
            int len = 0;
            foreach(var v in str.Split('%'))
            {
                if ((v[0] >= '0') && (v[0] <= '9'))
                    len += v[0] - '0';
                else
                    len++;
            }
            return len;
        }

        public void AddCmd(char key,string recvtype,string sendtype,Action<char,int,int[]> callback=null)
        {
            senddatatype.Add(key, sendtype);
            recvdatatype.Add(key, recvtype);
            recvdatalen.Add(key, recvtype.Split('%').Length-1);
            recvpacketlen.Add(key, packetlen(recvtype));
            recvcallback.Add(key, callback);
        }

        public sersonhelper()
        {
            //AddCmd('t', ">%2x%c%2x",">fet");
            //AddCmd('B', ">%2x%c%4x", ">%2x%c");
            //AddCmd('C', ">%2x%c", ">%2x%c%4x");
            //AddCmd('E', ">%2x%c", ">%2x%c");
            //AddCmd('T', ">%2x%c", ">%2x%c%2x");
            //AddCmd('S', ">%2x%c%2x%2x", ">%2x%c%2x%2x");
        }

        public static UInt16 cal_crc(string str, int length = -1)
        {
            UInt16 itemp = 0xFFFF;
            byte i;
            int index = 0;
            if (null == str) return 0;
            if (length == -1) length = str.Length;
            while (length-- != 0)
            {
                itemp ^= str[index++];
                for (i = 0; i < 8; i++)
                {
                    if ((itemp & 0x1) != 0)
                    {
                        itemp >>= 1;
                        itemp ^= 0xA001;
                    }
                    else
                    {
                        itemp >>= 1;
                    }
                }
            }
            //itemp = (UInt16)(((itemp << 8) | (itemp >> 8)) & 0xffff);
            return itemp;
        }

        public int[] this[string str]
        {
            get
            {
                var data = new int[3];
                try
                {
                    formatRead(str, ">%2x%c", data);
                    if (recvdatatype.ContainsKey((char)data[1]))
                    {
                        int len = recvpacketlen.ContainsKey((char)data[1]) ? recvpacketlen[(char)data[1]] : 0;
                        int[] dat = new int[11];
                        dat[0] = data[0];
                        dat[1] = data[1];
                        data = dat;
                        int rcrc = (readhex(str, 2, len + 2) | (readhex(str, 2, len) << 8));
                        int lcrc = ((rcrc >> 8) | (rcrc << 8)) & 0xffff;
                        int crc = cal_crc(str, len);
                        if (crc != rcrc && crc != lcrc)
                            return null;
                    }
                    else
                    {
                        int len = str.Length - 1;
                        while (str[len] == '\r' || str[len] == '\n')
                            len--;
                        int rcrc = (readhex(str, 2, len - 1) | (readhex(str, 2, len - 3) << 8));
                        int lcrc = ((rcrc >> 8) | (rcrc << 8)) & 0xffff;
                        int crc = cal_crc(str, len - 3);
                        if (crc != rcrc && crc != lcrc)
                            return null;
                        data[2] = len - 3 - 4;
                    }
                    char key = (char)data[1];
                    int id = data[0];
                    int packetlen = recvdatalen[key];
                    if (packetlen != formatRead(str, recvdatatype[key], data))
                        return null;

                    if (recvcallback[key] != null)
                    {
                        int[] newdata = new int[packetlen-2];
                        for (int i = 2; i < packetlen; i++)
                            newdata[i-2] = data[i];
                        recvcallback[key](key, id, newdata);
                    }
                    return data;
                }
                catch { }
                return data;
            }
            set { }
        }

        public object this[char cmd, int id = 0, params int[] data]
        {
            get
            {
                string str = "";
                try
                {
                    if (senddatatype.ContainsKey(cmd))
                    {
                        str = formatWrite(cmd, id, senddatatype[cmd], data);
                    }
                }
                catch { }
                if (str.Length != 0)
                {
                    str += string.Format("{0:X4}", cal_crc(str));
                    str += "\r\n";
                }
                return str;
            }
            set
            {
            }
        }

        void recv()
        {

        }
    }
}
